﻿// Inner Fire 游戏引擎库
// Model - 用于载入模型
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层(4)
// 用于模型载入。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-01-17

#pragma once

#include <DirectXMath.h>
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h> // 包含Assimp的后处理步骤
#include <assimp/scene.h>       // 包含Assimp的场景结构体
#include <dx/mesh/dx_mesh.h>
#include <dx/mesh/geometry_generator.h>
#include <dx/mesh/skin_animator.h>
#include <game/animation/armature.h>
#include <game/resource/model_material.h>
#include <game/resource/resource.h>
#include <graphics/vertex.h>
#include <graphics/vertex_list.h>
#include <map>
#include <ref_info.h>
#include <xml_reader.h>

using namespace ikit;

namespace ifire::game {
class Assets;
class Avatar;
class BinaryModel;

enum class ModelFileType {
  Model = 0, // 普通的模型文件，FBX等
  Shape,     // 通过Shape的XML文件定义
  SkinMesh,  // 蒙皮骨骼
  Count
};

class Model : public Resource {
public:
  Model(Assets* manager, const XmlNode& node);
  Model(const Model& lhs) = delete;
  Model& operator=(const Model& lhs) = delete;
  ~Model();

  friend class BinaryModel;

  // 这个没有实际意义，只是为了匹配泛型
  void Ready() {}
  // 设置项目
  void Setup() override;

  // 载入此项目
  void Load(ID3D12Device* device, ID3D12GraphicsCommandList* cmd_list);

  //
  // 属性访问
  //
  dx::DxMesh* GetMesh() const;
  ModelFileType GetFileType() const { return file_type_; }
  ModelMaterialList& GetMaterials() { return materials_; }
  Armature* GetArmature() const { return armature_.get(); }
  size_t GetBoneCount() const;

  // 通过名称解析文件类型枚举
  static ModelFileType ReadFileType(ikit::sstr name);

public:
  int mIndex;
  std::string mName;
  bool mEnable;
  MatrixList mAnimeTransform;

private:
  // 当此项目是Shape时载入
  void LoadShape(ID3D12Device* device, ID3D12GraphicsCommandList* cmd_list);
  // 当此项目是Model时载入
  void LoadModel(ID3D12Device* device, ID3D12GraphicsCommandList* cmd_list);
  // 当项目是蒙皮骨骼的时候载入
  void LoadAssimp(ID3D12Device* device, ID3D12GraphicsCommandList* cmd_list);
  // 将已经载入的项目装配
  void SetupLoadInfo(ID3D12Device* device, ID3D12GraphicsCommandList* cmd_list);

private:
  // 用于载入的Scene
  const aiScene* scene_;
  // 此模型对应的文件类型
  ModelFileType file_type_;
  // 此模型对应的文件
  std::string filename_;
  // 此模型对应的通用骨架
  ikit::rinfo<Avatar> avatar_;

  // 此模型对应的Shape
  eastl::vector<dx::GeometrySetting> shapes;
  // 项目的Mesh配置
  ModelMaterialList materials_;
  // 项目的顶点列表
  VertexList<Vertex> vlist_;

  // ------
  // 用于蒙皮骨骼
  // ------
  // 是否需要对顶点权重进行归一化（有些模型需要修复）
  bool is_normalize_weights_;
  // 是否需要将骨骼的Node进行优化
  bool is_simplify_node_;
  // 是否需要变换单位。每单位从1cm变换为引擎的1m。
  bool convert_units_;
  // 是否缓存这个模型
  bool is_cache_;
  //

  // 此节点用于计算RootMotion的Node。通常是根结点
  ikit::sstr root_node_;
  // 项目的骨骼
  ArmaturePtr armature_;
  // 项目的顶点（专用于蒙皮骨骼）
  VertexList<SkinnedVertex> svlist_;
};

using ModelPtr = std::unique_ptr<Model>;

} // namespace ifire::game